#ifndef PBVP_DCT_ATTRIBUTE_H
#define PBVP_DCT_ATTRIBUTE_H

#include <iostream>
#include <string>

#include "../contree/bettinumber.h"

namespace PBVP {
	/// Base class of dual node attribute
	/**
	* This is an abstract class
	*/
	class Attribute
	{
	public:
		/// Constructor
		Attribute(void) {}

		/// Destructor
		virtual ~Attribute(void) {}

		/// similarity score between two attributes
		/**
		* pure virtual function
		* 
		* @param attr: pointer to another attribute
		* 
		* @return float between 0 and 1
		*/
		virtual float simScore(const Attribute* attr) = 0;

		/// Print the attribute
		virtual void print() = 0;

		/// Output the attribute to a string
		virtual std::string toString()
		{
			std::string str;
			return str;
		}

		/// Merge the attribute of a child node into that of its parent
		virtual void merge(const Attribute* childAttribute, bool inUpperRange) = 0;

		/// Make a clone of this attribute
		virtual Attribute* clone() = 0;

		/// raise the attribute of a child to that of a parent
		/**
		* Some fields of a child attribute are reset for the parent
		* attribute. For example, if the child is in the upper
		* sub-range, its inner surface is ignored.
		*/
		virtual Attribute* raise(bool inUpperRange) = 0;
	};

	/// Geometrical attribute of a DCT node
	/**
	* Geometrical attributes include the areas of the inner and outer bounding 
	* surface and the volume of the corresponding interval volume
	*/
	class GeoAttribute : public Attribute
	{
	public:
		/// Constructor
		GeoAttribute::GeoAttribute() 
		{
			lowerArea = 0.0f;
			upperArea = 0.0f;
			volume = 0.0f;
		}

		/// Compute similarity score
		/**
		*
		* @param attr: pointer to another attribute
		*
		* @return float between 0 and 1
		*/
		virtual float simScore(const Attribute *attr);

		/// Print the attribute
		virtual void print();

		///
		virtual std::string toString();

		/// Merge the attribute of a child node into that of its parent
		virtual void merge(const Attribute* childAttribute, bool inUpperRange);

		/// Make a clone of this attribute
		virtual Attribute* clone();

		/// raise the attribute of a child to that of a parent
		/**
		* Some fields of a child attribute are reset for the parent
		* attribute. For example, if the child is in the upper
		* sub-range, its inner surface is ignored.
		*/
		virtual Attribute* raise(bool inUpperRange);

		/// Export to DCT file
		friend std::ostream& operator << (std::ostream& out, const GeoAttribute& geoAttr)
		{
			out << "GeoAttribute: ";
			out << geoAttr.lowerArea << " " << geoAttr.upperArea << " " << geoAttr.volume << std::endl;
			return out;
		}

		/// Import from DCT file
		friend std::istream& operator >> (std::istream& in, GeoAttribute& geoAttr) 
		{
			std::string name;
			in >> name;
			in >> geoAttr.lowerArea >> geoAttr.upperArea >> geoAttr.volume;
			return in;
		}

		/// Area of inner surface
		float lowerArea;
		/// Area of the outer surface
		float upperArea;
		/// volume of the interval volume
		float volume;
	};

	/// Topological attribute of a DCT node
	/**
	* Betti numbers of the inner and outer bounding surfaces
	*/
	class TopoAttribute : public Attribute
	{
	public:
		/// Default Constructor
		TopoAttribute() {}

		/// Compute similarity score
		/**
		*
		* @param attr: pointer to another attribute
		*
		* @return float between 0 and 1
		*/
		virtual float simScore(const Attribute *attr);

		/// Print the attribute
		virtual void print();

		/// Output the attribute to a string
		virtual std::string toString();

		/// Merge the attribute of a child node into that of its parent
		virtual void merge(const Attribute* childAttribute, bool inUpperRange);

		/// Make a clone of this attribute
		virtual Attribute* clone();

		/// raise the attribute of a child to that of a parent
		/**
		* Some fields of a child attribute are reset for the parent
		* attribute. For example, if the child is in the upper
		* sub-range, its inner surface is ignored.
		*/
		virtual Attribute* raise(bool inUpperRange);

		/// Export to a DCT file
		friend std::ostream& operator << (std::ostream& out, const TopoAttribute &topo) {
			out << "TopoAttribute: ";
			out << topo.innerBetti << topo.outerBetti << std::endl;
			return out;
		}

		/// Import from a DCT file
		friend std::istream& operator >> (std::istream& in, TopoAttribute &topo) {
			std::string name;
			in >> name;
			in >> topo.innerBetti >> topo.outerBetti;
			return in;
		}

		/// Betti number of the inner surface
		BettiNumber innerBetti;
		/// Betti number of the outer surface
		BettiNumber outerBetti;
	};

	class Inertia;

	/// Shape attributes of a DCT node
	/**
	* mass inertia of the shape
	* more attributes to be added
	*/
	class ShapeAttribute : public Attribute
	{
	public:
		/// Default Constructor
		ShapeAttribute() {
			volume = 0;
			x_Integral = y_Integral = z_Integral = 0;
			x2_Integral = y2_Integral = z2_Integral = 0;
			xy_Integral = xz_Integral = yz_Integral = 0;
		}

		/// /= operator divide each interagl by a constant 
		const ShapeAttribute& operator /= (float vol)
		{
			volume /= vol;
			x_Integral /= vol;
			y_Integral /= vol;
			z_Integral /= vol;
			x2_Integral /= vol;
			y2_Integral /= vol;
			z2_Integral /= vol;
			xy_Integral /= vol;
			xz_Integral /= vol;
			yz_Integral /= vol;

			return *this;
		}
		/**
		*
		* @param attr: pointer to another attribute
		*
		* @return float between 0 and 1
		*/
		virtual float simScore(const Attribute *attr);

		/// Print the attribute
		virtual void print();

		/// Output the attribute to a string
		virtual std::string toString();

		/// Merge the attribute of a child node into that of its parent
		virtual void merge(const Attribute *childAttribute, bool inUpperRange);

		/// Make a clone of this attribute
		virtual Attribute * clone();

		/**
		* Some fields of a child attribute are reset for the parent
		* attribute. For example, if the child is in the upper
		* sub-range, its inner surface is ignored.
		*/
		virtual Attribute * raise(bool inUpperRange);

		/// compute inertia from shape attribute 
		Inertia calcInertia();

		/// Export shape attribute to a DCT file
		friend std::ostream& operator << (std::ostream& out, const ShapeAttribute& shape)
		{
			out << "ShapeAttribute: ";
			out << shape.volume << " ";
			out << shape.x_Integral << " " << shape.y_Integral << " " << shape.z_Integral << " ";
			out << shape.x2_Integral << " " << shape.y2_Integral << " " << shape.z2_Integral << " ";
			out << shape.xy_Integral << " " << shape.xz_Integral << " " << shape.yz_Integral << std::endl;
			return out;
		}

		/// Import shape attribute from a file
		friend std::istream& operator >> (std::istream& in, ShapeAttribute& shape)
		{
			std::string name;
			in >> name;
			in >> shape.volume;
			in >> shape.x_Integral >> shape.y_Integral >> shape.z_Integral;
			in >> shape.x2_Integral >> shape.y2_Integral >> shape.z2_Integral;
			in >> shape.xy_Integral >> shape.xz_Integral >> shape.yz_Integral;
			return in;
		}

		/// volume of the interval volume
		float volume;
		/// Integrals of x, y and z coodrinates
		float x_Integral, y_Integral, z_Integral;
		/// Integrals of x^2, y^2 and z^2
		float x2_Integral, y2_Integral, z2_Integral;
		/// Integral of xy, xz, and yz
		float xy_Integral, xz_Integral, yz_Integral;
	};

	class Multipole;

	/// Additional Potential attribute of a DCT node
	class PotentialAttribute : public Attribute
	{
	public:
		/// Default Constructor
		PotentialAttribute() {
			volume = 0.0f;
			x_Integral = y_Integral = z_Integral = 0.0f;
			p_Integral = 0.0f;
			px_Integral = py_Integral = pz_Integral = 0.0f;
			px2_Integral = py2_Integral = pz2_Integral = 0.0f;
			pxy_Integral = pxz_Integral = pyz_Integral = 0.0f;
		}
		/**
		*
		* @param attr: pointer to another attribute
		*
		* @return float between 0 and 1
		*/
		virtual float simScore(const Attribute *attr);

		/// Print the attribute
		virtual void print();

		/// Output the attribute to a string
		virtual std::string toString();

		/// Merge the attribute of a child node into that of its parent
		virtual void merge(const Attribute *childAttribute, bool inUpperRange);

		/// Make a clone of this attribute
		virtual Attribute * clone();

		/**
		* Some fields of a child attribute are reset for the parent
		* attribute. For example, if the child is in the upper
		* sub-range, its inner surface is ignored.
		*/
		virtual Attribute * raise(bool inUpperRange);

		/// Compute multipole from the potential attributes
		Multipole calcMultipole();

		/// Export potential attribute to a file
		friend std::ostream& operator << (std::ostream& out, const PotentialAttribute& pot)
		{
			out << "PotentialAttribute: ";
			out << pot.volume << " ";
			out << pot.x_Integral << " " << pot.y_Integral << " " << pot.z_Integral << " ";
			out << pot.p_Integral << " ";
			out << pot.px_Integral << " " << pot.py_Integral << " " << pot.pz_Integral << " ";
			out << pot.px2_Integral << " " << pot.py2_Integral << " " << pot.pz2_Integral << " ";
			out << pot.pxy_Integral << " " << pot.pxz_Integral << " " << pot.pyz_Integral << std::endl;
			return out;
		}

		/// Import potential attribute from a file
		friend std::istream& operator >> (std::istream& in, PotentialAttribute& pot)
		{
			std::string name;
			in >> name;
			in >> pot.volume;
			in >> pot.x_Integral >> pot.y_Integral >> pot.z_Integral;
			in >> pot.p_Integral;
			in >> pot.px_Integral >> pot.py_Integral >> pot.pz_Integral;
			in >> pot.px2_Integral >> pot.py2_Integral >> pot.pz2_Integral;
			in >> pot.pxy_Integral >> pot.pxz_Integral >> pot.pyz_Integral;
			return in;
		}

		/// Total volume of the region
		float volume;
		/// Integrals of x, y and z coodrinates
		float x_Integral, y_Integral, z_Integral;
		/// Integral of the Potential
		float p_Integral;
		/// Integral of p*x, p*y, and p*z. used for dipole computation.
		float px_Integral, py_Integral, pz_Integral;
		/// Integral of p*x^2, p*y^2, p*z^2. Used for quadrupole computation.
		float px2_Integral, py2_Integral, pz2_Integral;
		/// Integral of p*x*y, p*x*z, and p*y*z
		float pxy_Integral, pxz_Integral, pyz_Integral;

	};

}
#endif
